Skip to content

feat(connect): Add Connect module#1597

Merged
gjtorikian merged 4 commits into
mainfrom
add-connect
May 26, 2026
Merged

feat(connect): Add Connect module#1597
gjtorikian merged 4 commits into
mainfrom
add-connect

Conversation

@gjtorikian
Copy link
Copy Markdown
Contributor

@gjtorikian gjtorikian commented May 20, 2026

Summary

  • Introduce a new Connect resource client on WorkOS for managing OAuth and M2M applications, application secrets, redirect URIs, user consent options, and the external auth login/consent flow.
  • Add generated interfaces, serializers, fixtures, and specs under src/connect/ from the latest oagen spec.
  • Export src/connect/interfaces from the package barrel so consumers can import the new types.

Test plan

  • npm test passes (unit + serializer specs)
  • npm run build succeeds with the new exports
  • workos.connect is reachable on the client and methods type-check from a consumer

Summary by CodeRabbit

  • New Features

    • WorkOS Connect client added with OAuth2 completion support
    • OAuth application management: create, list (paginated), retrieve, update, delete
    • M2M application creation and management
    • Application client secret creation and deletion
    • List applications supports pagination and optional organization filtering
  • Tests

    • Added test coverage for Connect flows (OAuth completion, application CRUD, and client-secret operations)

Review Change Stack

@gjtorikian gjtorikian requested review from a team as code owners May 20, 2026 19:48
@gjtorikian gjtorikian requested a review from ericroberts May 20, 2026 19:48
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e9dfc2fe-dc92-4d25-af0b-e98976f761d9

📥 Commits

Reviewing files that changed from the base of the PR and between d58152d and 910e908.

📒 Files selected for processing (4)
  • .oagen-manifest.json
  • src/connect/interfaces/connect-application.interface.ts
  • src/index.ts
  • src/workos.ts

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


📝 Walkthrough

Walkthrough

This PR adds a complete Connect API client module to the WorkOS SDK with TypeScript interfaces, serializers, a Connect client class (CRUD, pagination, OAuth completion, client-secret management), JSON fixtures and Jest tests, and public SDK integration plus manifest updates.

Changes

Connect API Client Implementation

Layer / File(s) Summary
Connect Data Type Contracts
src/connect/interfaces/*
TypeScript interfaces and a barrel export define domain models (camelCase with Date) and wire response shapes (snake_case with string timestamps) for user objects, consent options, redirect URIs, OAuth/M2M applications, application secrets, login requests, and list options.
Serialization Layer
src/connect/serializers/*
Serializers convert between domain models and API wire formats (camelCase↔snake_case), handling Date↔ISO string conversions and null-safe mappings for nested structures across Connect types.
Connect API Client Implementation
src/connect/connect.ts
Connect client class implements completeOAuth2, listApplications (auto-pagination + org filter), createApplication (dispatches OAuth vs M2M), createOAuthApplication/createM2MApplication helpers, get/update/delete application, and application client-secret list/create/delete methods using shared fetch/util and serializers.
Test Fixtures and Test Suite
src/connect/fixtures/*, src/connect/connect.spec.ts
JSON fixtures for requests/responses and Jest tests cover OAuth2 completion, application CRUD flows, and client-secret operations with request path/body assertions and response deserialization/timestamp checks.
Public API Export and WorkOS Integration
src/index.ts, src/workos.ts, .oagen-manifest.json
Exports Connect interfaces from the public barrel, adds a readonly connect sub-client on WorkOS, and updates the OAGen manifest to include the new Connect files.

Suggested reviewers

  • stacurry
  • faroceann
  • imkesin
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a new Connect module to the WorkOS SDK.
Description check ✅ Passed The pull request description is comprehensive and covers the key objectives, but does not follow the template structure requiring a Documentation section with checkbox.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-connect

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 20, 2026

Greptile Summary

This PR introduces a new Connect module to the WorkOS Node.js SDK, wiring up workos.connect with methods for managing OAuth and M2M applications, client secrets, and the standalone completeOAuth2 flow.

  • connect.ts exposes listApplications, createApplication (with typed discriminated dispatch), getApplication, updateApplication, deleteApplication, listApplicationClientSecrets, createApplicationClientSecret, deleteClientSecret, and completeOAuth2. Two additional convenience methods (createOAuthApplication, createM2MApplication) build wire bodies by hand, bypassing the generated serializers and behaving differently for null vs absent optional fields compared to the primary createApplication path.
  • Serializers and interfaces are generated by oagen and follow the established camelCase ↔ snake_case pattern; update-oauth-application.serializer.ts serializes every optional field with ?? null, meaning a partial update payload will explicitly clear existing fields on the API side. The connect-application.serializer.ts file is inadvertently absent from the serializers barrel.

Confidence Score: 3/5

The new Connect module is mostly well-structured, but several issues in connect.ts and the update serializer produce wrong wire payloads that would silently affect real API calls.

The auto-pagination path passes camelCase options to the API for pages beyond the first, so organizationId filtering silently breaks for multi-page results. The createOAuthApplication/createM2MApplication convenience methods build wire bodies by hand and diverge from the serializer behavior for null fields. The update serializer converts every absent optional field to explicit null, which would clear existing description, scopes, and redirect URIs on a REST PUT even when the caller only wanted to rename the application.

src/connect/connect.ts (auto-pagination options and convenience method bodies), src/connect/serializers/update-oauth-application.serializer.ts (null-clearing on partial updates)

Important Files Changed

Filename Overview
src/connect/connect.ts Core Connect client: auto-pagination drops organizationId filter on pages 2+; createOAuthApplication/createM2MApplication bypass typed serializers with hand-rolled bodies
src/connect/serializers/update-oauth-application.serializer.ts Partial update serializer sends explicit null for every omitted optional field, which would clear existing description/scopes/redirect_uris on a typical REST PUT
src/connect/serializers/index.ts Serializers barrel is missing connect-application.serializer.ts — minor inconsistency, no runtime breakage since connect.ts imports directly
src/connect/serializers/connect-application.serializer.ts Correctly dispatches deserialization on application_type discriminant with a throwing default; serialize direction lacks that default case
src/connect/connect.spec.ts Good coverage for main methods; createOAuthApplication and createM2MApplication convenience methods have no dedicated tests
src/connect/interfaces/index.ts ListApplicationsOptions missing from barrel (noted in prior review thread); all other interfaces correctly re-exported
src/workos.ts Registers the new Connect client on the WorkOS class; straightforward addition alongside existing module registrations
src/index.ts Adds connect/interfaces barrel re-export to the package public API; no issues

Sequence Diagram

sequenceDiagram
    participant Consumer
    participant WorkOS SDK (connect.ts)
    participant WorkOS API

    Consumer->>WorkOS SDK (connect.ts): completeOAuth2(payload)
    WorkOS SDK (connect.ts)->>WorkOS SDK (connect.ts): serializeUserManagementLoginRequest(payload)
    WorkOS SDK (connect.ts)->>WorkOS API: POST /authkit/oauth2/complete
    WorkOS API-->>WorkOS SDK (connect.ts): { redirect_uri }
    WorkOS SDK (connect.ts)->>WorkOS SDK (connect.ts): deserializeExternalAuthCompleteResponse(data)
    WorkOS SDK (connect.ts)-->>Consumer: ExternalAuthCompleteResponse { redirectUri }

    Consumer->>WorkOS SDK (connect.ts): listApplications(options)
    WorkOS SDK (connect.ts)->>WorkOS SDK (connect.ts): serializeListApplicationsOptions(options)
    WorkOS SDK (connect.ts)->>WorkOS API: GET /connect/applications?...
    WorkOS API-->>WorkOS SDK (connect.ts): { data: [...], listMetadata }
    WorkOS SDK (connect.ts)->>WorkOS SDK (connect.ts): deserializeConnectApplication(each)
    WorkOS SDK (connect.ts)-->>Consumer: AutoPaginatable<ConnectApplication>

    Consumer->>WorkOS SDK (connect.ts): createApplication({ applicationType: 'oauth'|'m2m', ... })
    WorkOS SDK (connect.ts)->>WorkOS SDK (connect.ts): switch(applicationType) → serializeCreate*Application
    WorkOS SDK (connect.ts)->>WorkOS API: POST /connect/applications
    WorkOS API-->>WorkOS SDK (connect.ts): ConnectApplicationResponse
    WorkOS SDK (connect.ts)->>WorkOS SDK (connect.ts): deserializeConnectApplication(data)
    WorkOS SDK (connect.ts)-->>Consumer: ConnectApplication

    Consumer->>WorkOS SDK (connect.ts): createApplicationClientSecret(id, payload)
    WorkOS SDK (connect.ts)->>WorkOS API: POST /connect/applications/{id}/client_secrets
    WorkOS API-->>WorkOS SDK (connect.ts): NewConnectApplicationSecretResponse
    WorkOS SDK (connect.ts)->>WorkOS SDK (connect.ts): deserializeNewConnectApplicationSecret(data)
    WorkOS SDK (connect.ts)-->>Consumer: NewConnectApplicationSecret { secret (one-time) }
Loading

Reviews (4): Last reviewed commit: "add docs on union type" | Re-trigger Greptile

Comment thread src/connect/interfaces/index.ts
Comment thread src/connect/connect.ts
Comment thread src/connect/connect.ts
Comment thread src/connect/connect.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
src/connect/connect.ts (1)

210-228: ⚡ Quick win

Manual body construction bypasses serialization logic.

Similar to createOAuthApplication, this method manually builds the request body instead of using serializeCreateM2MApplication (imported at line 51). While M2M applications don't have nested objects requiring serialization like OAuth applications do, this is still inconsistent with:

  • The createApplication method (lines 139-163) which uses serializers
  • The updateApplication method (lines 257-269) which uses serializers
♻️ Recommended refactor to use the serializer
 async createM2MApplication(
   name: string,
   organizationId: string,
   description?: string | null,
   scopes?: string[] | null,
 ): Promise<ConnectApplication> {
-  const body: Record<string, unknown> = {
-    application_type: 'm2m',
-    name: name,
-    organization_id: organizationId,
-  };
-  if (description !== undefined) body.description = description;
-  if (scopes !== undefined) body.scopes = scopes;
-  const { data } = await this.workos.post<ConnectApplicationResponse>(
+  const payload: CreateM2MApplication = {
+    applicationType: 'm2m',
+    name,
+    organizationId,
+    ...(description !== undefined && { description }),
+    ...(scopes !== undefined && { scopes }),
+  };
+  const { data } = await this.workos.post<ConnectApplicationResponse, CreateM2MApplicationResponse>(
     '/connect/applications',
-    body,
+    serializeCreateM2MApplication(payload),
   );
   return deserializeConnectApplication(data);
 }
src/connect/serializers.spec.ts (1)

43-43: ⚡ Quick win

Type safety bypassed with as any cast.

The as any cast defeats TypeScript's type checking. While this may be intentional in generated code to handle minor fixture/interface mismatches, it means type errors in serializers won't be caught by these tests.

If oagen controls the generation, consider ensuring fixtures exactly match their corresponding interfaces to avoid needing as any.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a6438bd4-4c7e-48e1-b5b9-0e0b42b27e73

📥 Commits

Reviewing files that changed from the base of the PR and between 9ebc818 and cda0161.

📒 Files selected for processing (49)
  • .oagen-manifest.json
  • src/connect/connect.spec.ts
  • src/connect/connect.ts
  • src/connect/fixtures/application-credentials-list-item.json
  • src/connect/fixtures/connect-application.json
  • src/connect/fixtures/create-application-secret.json
  • src/connect/fixtures/create-m2m-application.json
  • src/connect/fixtures/create-oauth-application.json
  • src/connect/fixtures/external-auth-complete-response.json
  • src/connect/fixtures/list-connect-application.json
  • src/connect/fixtures/new-connect-application-secret.json
  • src/connect/fixtures/redirect-uri-input.json
  • src/connect/fixtures/update-oauth-application.json
  • src/connect/fixtures/user-consent-option-choice.json
  • src/connect/fixtures/user-consent-option.json
  • src/connect/fixtures/user-management-login-request.json
  • src/connect/fixtures/user-object.json
  • src/connect/interfaces/application-credentials-list-item.interface.ts
  • src/connect/interfaces/connect-application.interface.ts
  • src/connect/interfaces/create-application-secret.interface.ts
  • src/connect/interfaces/create-m2m-application.interface.ts
  • src/connect/interfaces/create-oauth-application.interface.ts
  • src/connect/interfaces/external-auth-complete-response.interface.ts
  • src/connect/interfaces/index.ts
  • src/connect/interfaces/list-applications-options.interface.ts
  • src/connect/interfaces/new-connect-application-secret.interface.ts
  • src/connect/interfaces/redirect-uri-input.interface.ts
  • src/connect/interfaces/update-oauth-application.interface.ts
  • src/connect/interfaces/user-consent-option-choice.interface.ts
  • src/connect/interfaces/user-consent-option.interface.ts
  • src/connect/interfaces/user-management-login-request.interface.ts
  • src/connect/interfaces/user-object.interface.ts
  • src/connect/serializers.spec.ts
  • src/connect/serializers/application-credentials-list-item.serializer.ts
  • src/connect/serializers/connect-application.serializer.ts
  • src/connect/serializers/create-application-secret.serializer.ts
  • src/connect/serializers/create-m2m-application.serializer.ts
  • src/connect/serializers/create-oauth-application.serializer.ts
  • src/connect/serializers/external-auth-complete-response.serializer.ts
  • src/connect/serializers/index.ts
  • src/connect/serializers/new-connect-application-secret.serializer.ts
  • src/connect/serializers/redirect-uri-input.serializer.ts
  • src/connect/serializers/update-oauth-application.serializer.ts
  • src/connect/serializers/user-consent-option-choice.serializer.ts
  • src/connect/serializers/user-consent-option.serializer.ts
  • src/connect/serializers/user-management-login-request.serializer.ts
  • src/connect/serializers/user-object.serializer.ts
  • src/index.ts
  • src/workos.ts

Comment thread src/connect/connect.ts
Comment thread src/connect/interfaces/application-credentials-list-item.interface.ts Outdated
Comment thread src/connect/interfaces/index.ts
Comment thread src/connect/interfaces/new-connect-application-secret.interface.ts Outdated
Comment thread src/connect/serializers.spec.ts Outdated
/** An ISO 8601 timestamp. */
updatedAt: Date;
/** The type of the application. */
applicationType?: 'm2m';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CreateM2MApplication and CreateOAuthApplication types look good, but this ConnectApplication type is a bit weird. I'd expect applicationType to be a discriminator between the two as the form a kind of union, but here it only lists m2m.

Seems like this modeling isn't quite right. Is there a way to get the generator to give us something better?

Comment thread src/connect/connect.ts
* @throws {UnprocessableEntityException} 422
*/
async createApplication(
payload: CreateOAuthApplication | CreateM2MApplication,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this type of union of the different applications is more what I was imagining. 💯

Copy link
Copy Markdown
Contributor

@mthadley mthadley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gjtorikian Overall, looks solid.

The only thing that looks off and that could be usability problem is this, so requesting changes until it can be addressed.

Comment on lines +9 to +19
export const serializeUpdateOAuthApplication = (
model: UpdateOAuthApplication,
): UpdateOAuthApplicationResponse => ({
name: model.name,
description: model.description ?? null,
scopes: model.scopes ?? null,
redirect_uris:
model.redirectUris != null
? model.redirectUris.map(serializeRedirectUriInput)
: null,
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Partial update silently clears existing optional fields

description, scopes, and redirect_uris are serialized with ?? null or a falsy-null fallback, so when a caller passes only { name: 'New Name' }, the wire body becomes { name: "New Name", description: null, scopes: null, redirect_uris: null }. A typical REST API that receives explicit null on a PUT treats it as "clear this field", meaning existing description, scopes, and redirect URIs would be erased even though the caller never touched them.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/connect/connect.ts (1)

176-200: ⚠️ Potential issue | 🔴 Critical | ⚖️ Poor tradeoff

Critical: Still manually constructing request body instead of using serializer.

This method manually builds the wire-format request body (lines 185-194) with snake_case keys (application_type, is_first_party, redirect_uris) instead of using serializeCreateOAuthApplication, which is imported at line 50. This was flagged in a previous review as addressed in commits c82e4e8 to 4c21b9c, but the manual construction is still present in the current code.

The manual approach has a critical flaw: line 192 assigns redirectUris directly without calling serializeRedirectUriInput on each item, which transforms the default field from undefined to null per the wire format contract.

This breaks consistency with:

  • createApplication (line 150) — uses serializeCreateOAuthApplication
  • updateApplication (line 266) — uses serializeUpdateOAuthApplication
🔧 Refactor to use the serializer
   async createOAuthApplication(
     name: string,
     isFirstParty: boolean,
     description?: string | null,
     scopes?: string[] | null,
     redirectUris?: RedirectUriInput[] | null,
     usesPkce?: boolean | null,
     organizationId?: string | null,
   ): Promise<ConnectApplication> {
-    const body: Record<string, unknown> = {
-      application_type: 'oauth',
-      name: name,
-      is_first_party: isFirstParty,
-    };
-    if (description !== undefined) body.description = description;
-    if (scopes !== undefined) body.scopes = scopes;
-    if (redirectUris !== undefined) body.redirect_uris = redirectUris;
-    if (usesPkce !== undefined) body.uses_pkce = usesPkce;
-    if (organizationId !== undefined) body.organization_id = organizationId;
-    const { data } = await this.workos.post<ConnectApplicationResponse>(
+    const payload: CreateOAuthApplication = {
+      applicationType: 'oauth',
+      name,
+      isFirstParty,
+      ...(description !== undefined && { description }),
+      ...(scopes !== undefined && { scopes }),
+      ...(redirectUris !== undefined && { redirectUris }),
+      ...(usesPkce !== undefined && { usesPkce }),
+      ...(organizationId !== undefined && { organizationId }),
+    };
+    const { data } = await this.workos.post<
+      ConnectApplicationResponse,
+      CreateOAuthApplicationResponse
+    >(
       '/connect/applications',
-      body,
+      serializeCreateOAuthApplication(payload),
     );
     return deserializeConnectApplication(data);
   }
🧹 Nitpick comments (4)
src/connect/fixtures/external-auth-complete-response.json (1)

2-2: ⚡ Quick win

Use a clearly non-secret state fixture value.

This JWT-like sample triggers secret scanners and creates unnecessary security noise in CI. Prefer an obvious test placeholder (e.g., state=test_state_value).

♻️ Suggested fixture update
-  "redirect_uri": "https://your-authkit-domain.workos.com/oauth/authorize/complete?state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0ZSI6InJhbmRvbV9zdGF0ZV9zdHJpbmciLCJpYXQiOjE3NDI2MDQ4NTN9.abc123def456ghi789"
+  "redirect_uri": "https://your-authkit-domain.workos.com/oauth/authorize/complete?state=test_state_value"
src/connect/fixtures/new-connect-application-secret.json (1)

8-8: ⚡ Quick win

Replace the secret-looking literal with an explicit test sentinel.

This value looks like a real credential and can trip leak detection tooling. Use an obviously fake fixture value (e.g., not_a_real_secret_for_tests).

♻️ Suggested fixture update
-  "secret": "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
+  "secret": "not_a_real_secret_for_tests"
src/connect/connect.spec.ts (2)

54-56: ⚡ Quick win

Assert against fixture fields instead of duplicating sensitive-looking literals.

These hardcoded values duplicate fixture content and increase false-positive leak noise. Assert against imported fixture properties to keep a single source of truth.

♻️ Suggested test update
-      expect(result.redirectUri).toBe(
-        'https://your-authkit-domain.workos.com/oauth/authorize/complete?state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0ZSI6InJhbmRvbV9zdGF0ZV9zdHJpbmciLCJpYXQiOjE3NDI2MDQ4NTN9.abc123def456ghi789',
-      );
+      expect(result.redirectUri).toBe(
+        externalAuthCompleteResponseFixture.redirect_uri,
+      );
...
-      expect(result.secret).toBe(
-        'abc123def456ghi789jkl012mno345pqr678stu901vwx234yz',
-      );
+      expect(result.secret).toBe(newConnectApplicationSecretFixture.secret);

Also applies to: 191-193


71-74: ⚡ Quick win

Tighten listApplications assertions to validate deserialization, not just shape.

This test currently verifies non-empty output but not field mapping. Reuse the helper on the first item and assert listMetadata values from fixture to catch serializer regressions.

♻️ Suggested test update
       expect(fetchSearchParams()).toHaveProperty('order');
       expect(Array.isArray(data)).toBe(true);
-      expect(listMetadata).toBeDefined();
       expect(data.length).toBeGreaterThan(0);
+      expectConnectApplicationM2M(data[0]);
+      expect(listMetadata).toEqual({ before: null, after: null });

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5f2b581e-0f09-4e0b-975a-5346dbe38cf5

📥 Commits

Reviewing files that changed from the base of the PR and between 7bf0e10 and d58152d.

📒 Files selected for processing (50)
  • .oagen-manifest.json
  • src/connect/connect.spec.ts
  • src/connect/connect.ts
  • src/connect/fixtures/application-credentials-list-item.json
  • src/connect/fixtures/connect-application.json
  • src/connect/fixtures/create-application-secret.json
  • src/connect/fixtures/create-m2m-application.json
  • src/connect/fixtures/create-oauth-application.json
  • src/connect/fixtures/external-auth-complete-response.json
  • src/connect/fixtures/list-connect-application.json
  • src/connect/fixtures/new-connect-application-secret.json
  • src/connect/fixtures/redirect-uri-input.json
  • src/connect/fixtures/update-oauth-application.json
  • src/connect/fixtures/user-consent-option-choice.json
  • src/connect/fixtures/user-consent-option.json
  • src/connect/fixtures/user-management-login-request.json
  • src/connect/fixtures/user-object.json
  • src/connect/interfaces/application-credentials-list-item.interface.ts
  • src/connect/interfaces/connect-application-redirect-uri.interface.ts
  • src/connect/interfaces/connect-application.interface.ts
  • src/connect/interfaces/create-application-secret.interface.ts
  • src/connect/interfaces/create-m2m-application.interface.ts
  • src/connect/interfaces/create-oauth-application.interface.ts
  • src/connect/interfaces/external-auth-complete-response.interface.ts
  • src/connect/interfaces/index.ts
  • src/connect/interfaces/list-applications-options.interface.ts
  • src/connect/interfaces/new-connect-application-secret.interface.ts
  • src/connect/interfaces/redirect-uri-input.interface.ts
  • src/connect/interfaces/update-oauth-application.interface.ts
  • src/connect/interfaces/user-consent-option-choice.interface.ts
  • src/connect/interfaces/user-consent-option.interface.ts
  • src/connect/interfaces/user-management-login-request.interface.ts
  • src/connect/interfaces/user-object.interface.ts
  • src/connect/serializers/application-credentials-list-item.serializer.ts
  • src/connect/serializers/connect-application-redirect-uri.serializer.ts
  • src/connect/serializers/connect-application.serializer.ts
  • src/connect/serializers/create-application-secret.serializer.ts
  • src/connect/serializers/create-m2m-application.serializer.ts
  • src/connect/serializers/create-oauth-application.serializer.ts
  • src/connect/serializers/external-auth-complete-response.serializer.ts
  • src/connect/serializers/index.ts
  • src/connect/serializers/new-connect-application-secret.serializer.ts
  • src/connect/serializers/redirect-uri-input.serializer.ts
  • src/connect/serializers/update-oauth-application.serializer.ts
  • src/connect/serializers/user-consent-option-choice.serializer.ts
  • src/connect/serializers/user-consent-option.serializer.ts
  • src/connect/serializers/user-management-login-request.serializer.ts
  • src/connect/serializers/user-object.serializer.ts
  • src/index.ts
  • src/workos.ts
✅ Files skipped from review due to trivial changes (32)
  • src/connect/fixtures/create-m2m-application.json
  • src/connect/serializers/create-m2m-application.serializer.ts
  • src/connect/fixtures/create-oauth-application.json
  • src/connect/serializers/application-credentials-list-item.serializer.ts
  • src/connect/fixtures/user-object.json
  • src/connect/fixtures/update-oauth-application.json
  • src/connect/fixtures/redirect-uri-input.json
  • src/connect/interfaces/list-applications-options.interface.ts
  • src/connect/interfaces/connect-application-redirect-uri.interface.ts
  • src/connect/fixtures/user-consent-option-choice.json
  • src/connect/interfaces/create-m2m-application.interface.ts
  • src/connect/fixtures/application-credentials-list-item.json
  • src/connect/serializers/create-oauth-application.serializer.ts
  • src/connect/fixtures/create-application-secret.json
  • src/connect/serializers/user-management-login-request.serializer.ts
  • src/connect/interfaces/user-consent-option.interface.ts
  • src/connect/serializers/new-connect-application-secret.serializer.ts
  • src/connect/interfaces/user-consent-option-choice.interface.ts
  • src/connect/interfaces/user-object.interface.ts
  • src/connect/interfaces/update-oauth-application.interface.ts
  • src/connect/interfaces/redirect-uri-input.interface.ts
  • src/connect/serializers/connect-application.serializer.ts
  • src/connect/serializers/user-consent-option-choice.serializer.ts
  • src/connect/interfaces/user-management-login-request.interface.ts
  • src/connect/serializers/user-consent-option.serializer.ts
  • src/connect/interfaces/connect-application.interface.ts
  • src/connect/interfaces/external-auth-complete-response.interface.ts
  • src/connect/serializers/external-auth-complete-response.serializer.ts
  • .oagen-manifest.json
  • src/connect/fixtures/user-management-login-request.json
  • src/connect/interfaces/application-credentials-list-item.interface.ts
  • src/connect/interfaces/create-application-secret.interface.ts

Comment thread src/connect/connect.ts
Comment on lines +210 to +228
async createM2MApplication(
name: string,
organizationId: string,
description?: string | null,
scopes?: string[] | null,
): Promise<ConnectApplication> {
const body: Record<string, unknown> = {
application_type: 'm2m',
name: name,
organization_id: organizationId,
};
if (description !== undefined) body.description = description;
if (scopes !== undefined) body.scopes = scopes;
const { data } = await this.workos.post<ConnectApplicationResponse>(
'/connect/applications',
body,
);
return deserializeConnectApplication(data);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚖️ Poor tradeoff

Critical: Manually constructing request body instead of using serializer.

This method manually builds the wire-format request body (lines 216-222) instead of using serializeCreateM2MApplication (imported at line 51). This is the same critical issue present in createOAuthApplication and breaks consistency with createApplication (line 152), which correctly uses the serializer when applicationType is 'm2m'.

🔧 Refactor to use the serializer
   async createM2MApplication(
     name: string,
     organizationId: string,
     description?: string | null,
     scopes?: string[] | null,
   ): Promise<ConnectApplication> {
-    const body: Record<string, unknown> = {
-      application_type: 'm2m',
-      name: name,
-      organization_id: organizationId,
-    };
-    if (description !== undefined) body.description = description;
-    if (scopes !== undefined) body.scopes = scopes;
-    const { data } = await this.workos.post<ConnectApplicationResponse>(
+    const payload: CreateM2MApplication = {
+      applicationType: 'm2m',
+      name,
+      organizationId,
+      ...(description !== undefined && { description }),
+      ...(scopes !== undefined && { scopes }),
+    };
+    const { data } = await this.workos.post<
+      ConnectApplicationResponse,
+      CreateM2MApplicationResponse
+    >(
       '/connect/applications',
-      body,
+      serializeCreateM2MApplication(payload),
     );
     return deserializeConnectApplication(data);
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async createM2MApplication(
name: string,
organizationId: string,
description?: string | null,
scopes?: string[] | null,
): Promise<ConnectApplication> {
const body: Record<string, unknown> = {
application_type: 'm2m',
name: name,
organization_id: organizationId,
};
if (description !== undefined) body.description = description;
if (scopes !== undefined) body.scopes = scopes;
const { data } = await this.workos.post<ConnectApplicationResponse>(
'/connect/applications',
body,
);
return deserializeConnectApplication(data);
}
async createM2MApplication(
name: string,
organizationId: string,
description?: string | null,
scopes?: string[] | null,
): Promise<ConnectApplication> {
const payload: CreateM2MApplication = {
applicationType: 'm2m',
name,
organizationId,
...(description !== undefined && { description }),
...(scopes !== undefined && { scopes }),
};
const { data } = await this.workos.post<
ConnectApplicationResponse,
CreateM2MApplicationResponse
>(
'/connect/applications',
serializeCreateM2MApplication(payload),
);
return deserializeConnectApplication(data);
}

createdAt: Date;
/** An ISO 8601 timestamp. */
updatedAt: Date;
applicationType: 'oauth';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, looks like we are missing a "description" somewhere for applicationType. Maybe something to track down later.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch. another bug around the discriminated union -- will fix

@gjtorikian gjtorikian merged commit 4011dd0 into main May 26, 2026
7 of 8 checks passed
@gjtorikian gjtorikian deleted the add-connect branch May 26, 2026 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants